package com.my.house.common;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.my.house.common.utils.ThreadLocalHolder;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import org.slf4j.MDC;

/**
 * @author yangll
 */
@Slf4j
@Component
public class LoggingFilter extends OncePerRequestFilter {

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String uri = request.getRequestURI();
        // 静态资源不打印日志
        return uri.startsWith("/static/") || uri.endsWith(".html") || uri.endsWith(".ico") || uri.endsWith("error");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 添加 traceId 到 MDC
        String traceId = IdUtil.simpleUUID();
        MDC.put("traceId", traceId);

        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

        StopWatch watch = new StopWatch();
        watch.start();
        filterChain.doFilter(requestWrapper, responseWrapper);
        watch.stop();

        String requestBody = readRequest(requestWrapper);

        int requestSize = request.getContentLength();
        if (requestSize == 0){
            requestSize = requestWrapper.getContentLength();
        }
        if (requestSize == 0){
            requestSize =  requestBody.getBytes().length;
        }

        int responseSize = responseWrapper.getContentAsByteArray().length;
        String httpSize = String.valueOf((requestSize + responseSize)/1024d);
        String uri = request.getRequestURI();
        String ua = request.getHeader(Constants.UA);
        String responseBody = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);

        responseWrapper.copyBodyToResponse();

        log.info("{} {} {},\n" +
                        "<<<<<<<<<<\n" +
                        "q={}, \n" +
                        "r={}, \n" +
                        "ip={}, t={}ms, requestSize={}, responseSize={}, httpSize={}, ua={}\n" +
                        ">>>>>>>>>>\n",
                request.getMethod(), uri, responseWrapper.getStatus(),
                requestBody,
                responseBody,
                getIpAddr(request),
                watch.getTotalTimeMillis(),
                requestSize,
                responseSize,
                httpSize,
                ua
        );
        // 移除 traceId 以避免内存泄漏
        MDC.remove("traceId");
        ThreadLocalHolder.remove();
    }

    private String readRequest(ContentCachingRequestWrapper request) {
        if ("GET".equals(request.getMethod())) {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet()) {

                sb.append(e.getKey()).append(":").append(Arrays.toString(e.getValue())).append(", ");
            }

            return sb.toString();
        } else if ("POST".equals(request.getMethod())) {
            return new String(request.getContentAsByteArray(), StandardCharsets.UTF_8);
        } else {
            return request.getMethod();
        }
    }

    private String getIpAddr(HttpServletRequest request) {
        String ipAddress;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (StrUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (StrUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StrUtil.isBlank(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if ("127.0.0.1".equals(ipAddress)) {
                    // 根据网卡取本机配置的IP
                    try {
                        InetAddress inet = InetAddress.getLocalHost();
                        ipAddress = inet.getHostAddress();
                    } catch (UnknownHostException e) {
                        log.error("获取ip地址异常", e);
                    }
                }
            }
            // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割 "***.***.***.***".length()
            if (StrUtil.length(ipAddress) > 15 && ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        return ipAddress;
    }
}
